package com.sudo.boot.service.sys.impl;

import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.If;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.sudo.boot.base.constant.enums.EnumStatus;
import com.sudo.boot.base.exception.BizException;
import com.sudo.boot.base.util.IfFail;
import com.sudo.boot.base.util.IfSuccess;
import com.sudo.boot.common.util.task.TaskMethod;
import com.sudo.boot.common.util.task.TaskUtil;
import com.sudo.boot.mapper.sys.entity.SysLocalJob;
import com.sudo.boot.mapper.sys.mapper.SysLocalJobMapper;
import com.sudo.boot.mapper.sys.model.bo.SysLocalJobBoQuery;
import com.sudo.boot.mapper.sys.model.bo.SysLocalJobBoSave;
import com.sudo.boot.mapper.sys.model.bo.SysLocalJobBoEditNoStatus;
import com.sudo.boot.mapper.sys.model.bo.SysLocalJobBoEditStatus;
import com.sudo.boot.service.sys.SysLocalJobService;
import com.sudo.boot.service.sys.jobservice.config.CronTaskRegistry;
import com.sudo.boot.service.sys.converter.SysLocalJobConverter;
import com.sudo.boot.service.sys.jobservice.runnable.BeanMethodRunnable;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

import static com.sudo.boot.mapper.sys.entity.table.SysLocalJobTableDef.SYS_LOCAL_JOB;

@Slf4j
@Service
public class SysLocalJobServiceImpl extends ServiceImpl<SysLocalJobMapper, SysLocalJob> implements SysLocalJobService {

    @Resource
    private CronTaskRegistry cronTaskRegistry;
    @Resource
    private SysLocalJobConverter sysLocalJobConverter;

    @Transactional(propagation = Propagation.SUPPORTS,rollbackFor = Exception.class)
    @Override
    public Page<SysLocalJob> querySysLocalJobByPage(SysLocalJobBoQuery bo) {
        QueryWrapper queryWrapper = QueryWrapper.create()
                .where(SYS_LOCAL_JOB.BEAN_NAME.eq(bo.getBeanName(), If::hasText))
                .and(SYS_LOCAL_JOB.METHOD_NAME.eq(bo.getMethodName(), If::hasText))
                .and(SYS_LOCAL_JOB.METHOD_PARAMS.like(bo.getMethodParams(), If::hasText))
                .and(SYS_LOCAL_JOB.STATUS.eq(bo.getStatus(), If::hasText))
                .orderBy(SYS_LOCAL_JOB.ID.desc());
        return mapper.paginate(bo.getPageNumber(), bo.getPageSize(), queryWrapper);
    }

    @Transactional(propagation = Propagation.SUPPORTS,rollbackFor = Exception.class)
    public List<SysLocalJob> getSysLocalJobByStatus(String status) {
        return mapper.selectListByCondition(SYS_LOCAL_JOB.STATUS.eq(status));
    }

    @Transactional(propagation = Propagation.SUPPORTS,rollbackFor = Exception.class)
    @Override
    public SysLocalJob querySysLocalJobById(Integer id) {
        return mapper.selectOneById(id);
    }

    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    @Override
    public void updateSysLocalJobStatus(SysLocalJobBoEditStatus bo) {
        SysLocalJob sysLocalJob = new SysLocalJob();
        sysLocalJob.setId(bo.getId());
        sysLocalJob.setStatus(bo.getStatus());
        if (!EnumStatus.ON.getCode().equals(sysLocalJob.getStatus())
                && !EnumStatus.OFF.getCode().equals(sysLocalJob.getStatus())){
            sysLocalJob.setStatus(EnumStatus.OFF.getCode());
        }
        //修改SysLocalJob时，判断修改之前的状态，如果状态不一致，则需要关闭或打开正在内存中的定时任务
        SysLocalJob oldSysLocalJob = mapper.selectOneById(bo.getId());
        if (!oldSysLocalJob.getStatus().equals(sysLocalJob.getStatus())){

            IfSuccess.dealInt(()->mapper.update(sysLocalJob),()->{
                //删除old Task
                BeanMethodRunnable beanMethodRunnable = new BeanMethodRunnable(oldSysLocalJob);
                cronTaskRegistry.removeCronTask(beanMethodRunnable);
                //打开
                if (EnumStatus.ON.getCode().equals(sysLocalJob.getStatus())) {
                    cronTaskRegistry.addCronTask(beanMethodRunnable, oldSysLocalJob.getCronExpression());
                }
            });

        }
    }

    @Override
    public Map<String, Map<String, TaskMethod>> getMethodAndParamCountList() {
        return TaskUtil.getTaskMethodParamCountMap();
    }

    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    @Override
    public void saveSysLocalJob(SysLocalJobBoSave bo) {
        SysLocalJob sysLocalJob = sysLocalJobConverter.toSysLocalJob(bo);
        sysLocalJob.setStatus(EnumStatus.OFF.getCode());

        IfFail.dealInt(() -> mapper.insert(sysLocalJob));
    }

    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    @Override
    public void updateSysLocalJobWithNoStatus(SysLocalJobBoEditNoStatus bo) {
        SysLocalJob sysLocalJob = sysLocalJobConverter.toSysLocalJob(bo);

        if (!EnumStatus.ON.getCode().equals(sysLocalJob.getStatus())
                && !EnumStatus.OFF.getCode().equals(sysLocalJob.getStatus())){
            sysLocalJob.setStatus(EnumStatus.OFF.getCode());
        }
        sysLocalJob.setBeanName(null);
        sysLocalJob.setMethodName(null);
        sysLocalJob.setStatus(null);

        IfFail.dealInt(() -> mapper.update(sysLocalJob));
    }


    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    @Override
    public void deleteSysLocalJob(Integer id) {
        SysLocalJob sysLocalJob = mapper.selectOneById(id);
        if (sysLocalJob != null) {
            IfSuccess.dealInt(()->mapper.deleteById(id),()->{
                try {
                    BeanMethodRunnable beanMethodRunnable = new BeanMethodRunnable(
                            sysLocalJob.getId(),
                            sysLocalJob.getBeanName(),
                            sysLocalJob.getMethodName()
                    );
                    cronTaskRegistry.removeCronTask(beanMethodRunnable);
                }catch (NoSuchBeanDefinitionException e){
                    log.error("找不到定义的Bean", e);
                }
            });
        }
    }

    @Override
    public String verifySysLocalJobExist(int currentJobId, String nextJobIds) {
        if (!StringUtils.hasText(nextJobIds)) {
            return null;
        }
        Set<Integer> jobIdSet = new HashSet<>();
        for (String s : nextJobIds.split(",")) {
            try {
                int jobId = Integer.parseInt(s.trim());
                jobIdSet.add(jobId);
            }catch (Exception e){
                throw new BizException("任务Id必须为int类型");
            }
        }
        if (jobIdSet.size() > 3) {
            throw new BizException("最多允许有3个子任务");
        }
        if (jobIdSet.contains(currentJobId)) {
            throw new BizException("子任务不能包含自己");
        }
        List<SysLocalJob> sysLocalJobList = mapper.selectListByIds(jobIdSet);
        for (SysLocalJob localJob : sysLocalJobList) {
            if (StringUtils.hasText(localJob.getNextJobIds())) {
                throw new BizException("任务【" + localJob.getId() + "】已存在子任务，不允许当做子任务执行");
            }
        }
        return jobIdSet.stream().sorted().map(String::valueOf).collect(Collectors.joining(","));
    }
}
